﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Brainee.Immutable
{
  // From Eric Lippert:
  // http://blogs.msdn.com/ericlippert/archive/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack.aspx
  public sealed class Stack<T> : IStack<T>
  {
    #region Empty stack

    private sealed class EmptyStack : IStack<T>
    {
      public bool IsEmpty { get { return true; } }
      public T Peek () { throw new Exception ("Empty stack"); }
      public IStack<T> Push (T value) { return new Stack<T> (value, this); }
      public IStack<T> Pop () { throw new Exception ("Empty stack"); }
      public IEnumerator<T> GetEnumerator () { yield break; }
      IEnumerator IEnumerable.GetEnumerator () { return this.GetEnumerator (); }
    }
    
    private static readonly EmptyStack empty = new EmptyStack ();
    
    public static IStack<T> Empty { get { return empty; } }

    #endregion
    #region Fields

    private readonly T head;
    private readonly IStack<T> tail;

    #endregion
    #region Constructor

    private Stack (T head, IStack<T> tail)
    {
      this.head = head;
      this.tail = tail;
    }

    #endregion
    #region IStack<T> implementation

    public bool IsEmpty { get { return false; } }
    public T Peek () { return head; }
    public IStack<T> Pop () { return tail; }
    public IStack<T> Push (T value) { return new Stack<T> (value, this); }

    #endregion
    #region IEnumerable<T> implementation

    public IEnumerator<T> GetEnumerator ()
    {
      for (IStack<T> stack = this; !stack.IsEmpty; stack = stack.Pop ())
        yield return stack.Peek ();
    }

    IEnumerator IEnumerable.GetEnumerator () { return this.GetEnumerator (); }

    #endregion
  }
}
